#coding:utf-8
"""
Created on 2018-7-23
@author: river
@env:    PyQt>5.9.5 python3.6.5
"""

import os, sys
import PyQt5 as Qt
import math

from PyQt5 import QtGui, QtWidgets, QtCore

import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as pltCanvas
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT as pltToolbar

class QMySimulateDlg(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.fig = plt.Figure()
        self.ax = self.fig.add_subplot(3, 1, 1)
        self.ax1 = self.fig.add_subplot(3, 1, 2)
        self.ax2 = self.fig.add_subplot(3, 1, 3)
        self.canvas = pltCanvas(self.fig)
        self.setupUI()

        self.rawData = []
        self.oldData = []
        self.newData = []
        self.algorithm = 0
        self.filename = ""
        self.readFlag = False

        self.algorithmArr = [self._erChenAlgo, self._elecRefreshAlgo, self._elecCompensateAlgo, self._zeroFollow, \
                             self._semiAlgorithmAlgo, self._autoCalibAlgo, self._infinityApproachAlgo, self._returnZeroAlgo,\
                             self._MODRCFilter]

    def setupUI(self):
        self.setWindowTitle("SensorAlgorithm")
        self.resize(1200, 600)
        self.setAutoFillBackground(True)

        mainlayout = QtWidgets.QVBoxLayout()
        #top layout
        #widgetTop = QtWidgets.QWidget(self)
        toplayout = QtWidgets.QHBoxLayout()
        #widgetTop.setLayout(toplayout)

        self.FileTxtEdit = QtWidgets.QTextEdit()
        self.FileTxtEdit.setFixedHeight(25)
        FileBtn = QtWidgets.QPushButton("打开文件")
        FileBtn.clicked.connect(self._open)
        AlgorithmBtn = QtWidgets.QPushButton("运行算法")
        AlgorithmBtn.clicked.connect(self._run)

        toplayout.addWidget(self.FileTxtEdit)
        toplayout.addWidget(FileBtn)
        toplayout.addWidget(AlgorithmBtn)

        #mainlayout.addWidget(widgetTop)
        mainlayout.addLayout(toplayout)

        #middle layout
        #widgetMiddle = QtWidgets.QWidget(self)
        middlelayout = QtWidgets.QHBoxLayout()
        #widgetMiddle.setLayout(middlelayout)

        #self.radioGroup = QtWidgets.QButtonGroup(widgetMiddle)
        self.radioGroup = QtWidgets.QButtonGroup(middlelayout)
        erChen = QtWidgets.QRadioButton("最小二乘", self)
        erChen.setFocusPolicy(QtCore.Qt.NoFocus)
        erChen.setChecked(True)
        erChen.toggled.connect(self._check)
        self.radioGroup.addButton(erChen, 0)
        middlelayout.addWidget(erChen)

        elecRefresh = QtWidgets.QRadioButton("电压更新", self)
        elecRefresh.setFocusPolicy(QtCore.Qt.NoFocus)
        elecRefresh.setChecked(False)
        elecRefresh.toggled.connect(self._check)
        self.radioGroup.addButton(elecRefresh, 1)
        middlelayout.addWidget(elecRefresh)

        elecCompensate = QtWidgets.QRadioButton("电压补偿", self)
        elecCompensate.setFocusPolicy(QtCore.Qt.NoFocus)
        elecCompensate.setChecked(False)
        elecCompensate.toggled.connect(self._check)
        self.radioGroup.addButton(elecCompensate, 2)
        middlelayout.addWidget(elecCompensate)

        zeroFollow = QtWidgets.QRadioButton("零点跟踪", self)
        zeroFollow.setFocusPolicy(QtCore.Qt.NoFocus)
        zeroFollow.setChecked(False)
        zeroFollow.toggled.connect(self._check)
        self.radioGroup.addButton(zeroFollow, 3)
        middlelayout.addWidget(zeroFollow)

        semiAlgorithm = QtWidgets.QRadioButton("半导体算法", self)
        semiAlgorithm.setFocusPolicy(QtCore.Qt.NoFocus)
        semiAlgorithm.setChecked(False)
        semiAlgorithm.toggled.connect(self._check)
        self.radioGroup.addButton(semiAlgorithm, 4)
        middlelayout.addWidget(semiAlgorithm)

        autoCalib = QtWidgets.QRadioButton("自动标定", self)
        autoCalib.setFocusPolicy(QtCore.Qt.NoFocus)
        autoCalib.setChecked(False)
        autoCalib.toggled.connect(self._check)
        self.radioGroup.addButton(autoCalib, 5)
        middlelayout.addWidget(autoCalib)

        infinityApproach = QtWidgets.QRadioButton("无限逼近", self)
        infinityApproach.setFocusPolicy(QtCore.Qt.NoFocus)
        infinityApproach.setChecked(False)
        infinityApproach.toggled.connect(self._check)
        self.radioGroup.addButton(infinityApproach, 6)
        middlelayout.addWidget(infinityApproach)

        returnZero = QtWidgets.QRadioButton("归零处理", self)
        returnZero.setFocusPolicy(QtCore.Qt.NoFocus)
        returnZero.setChecked(False)
        returnZero.toggled.connect(self._check)
        self.radioGroup.addButton(returnZero, 7)
        middlelayout.addWidget(returnZero)

        RCFilter = QtWidgets.QRadioButton("一阶低通", self)
        RCFilter.setFocusPolicy(QtCore.Qt.NoFocus)
        RCFilter.setChecked(False)
        RCFilter.toggled.connect(self._check)
        self.radioGroup.addButton(RCFilter, 8)
        middlelayout.addWidget(RCFilter)

        #mainlayout.addWidget(widgetMiddle)
        mainlayout.addLayout(middlelayout)

        #bottom layout
        #widgetBottom = QtWidgets.QWidget(self)
        bottomlayout = QtWidgets.QVBoxLayout()
        #widgetBottom.setLayout(bottomlayout)

        toolbar = pltToolbar(self.canvas, self)
        bottomlayout.addWidget(toolbar)
        bottomlayout.addWidget(self.canvas)

        mainlayout.addLayout(bottomlayout)
        mainlayout.setSpacing(10)
        #main setlayout
        main_widget = QtWidgets.QWidget()
        main_widget.setLayout(mainlayout)
        self.setCentralWidget(main_widget)

    def _check(self):
        self.algorithm = self.radioGroup.checkedId()

    def _open(self):
        fl, _ = QtWidgets.QFileDialog.getOpenFileName(self, "选择文件", "", "Txt File(*.txt)")
        if fl:
            self.filename = fl
            self.FileTxtEdit.setText(self.filename)
            self._read()

    def _read(self):
        self.rawData = []
        try:
            with open(self.filename) as f:
                for i, s in enumerate(f):
                    s = s.strip()
                    v = s.rsplit(",")
                    if v[0].isdigit():
                        self.rawData.append(int(v[0]))
                        self.readFlag = True
            #rawData must more than 10
            if len(self.rawData) < 10: self.readFlag = False
        except Exception as e:
            QtWidgets.QMessageBox.warning(self, "warning", str(e)[:64])

    def _filter(self, data, filter=5):
        ff = []
        if len(data) < filter:  return ff

        ssum = sum(data[:filter])
        ff.append(ssum / filter)
        for i in range(len(data) - filter):
            ssum -= data[i]
            ssum += data[i + filter]
            ff.append(ssum / filter)
        return ff

    def _run(self):
        '''
        plt draw function
        legend(loc='best')(only implemented for axes legends)(自适应方式)
        '''
        if self.readFlag:
            try:
                ttitle = self.algorithmArr[self.algorithm]()
                # print("old={0}, new={1}".format(len(self.oldData), len(self.newData)))
                # f = self._filter(self.rawData, 10)
                # self.ax1.plot(range(len(self.newData)), self.newData, 'r', range(len(f)), f, 'c')
                self.ax.cla()
                self.ax.set_title(ttitle)
                self.ax.plot(range(len(self.oldData)), self.oldData, 'b')
                self.ax.legend(["old"])  # default loc="upper right")
                self.ax.grid(True)

                self.ax1.cla()
                self.ax1.plot(range(len(self.newData)),self.newData, 'r')
                self.ax1.legend(["new"])
                self.ax1.grid(True)

                self.ax2.cla()
                self.ax2.plot(range(len(self.oldData)), self.oldData, 'b', range(len(self.newData)),self.newData, 'r')
                self.ax2.grid(True)

                # self.ax2 valid
                #bottom, top = self.ax.get_ylim()
                #bottom1, top1 = self.ax1.get_ylim()
                #minbottom = min(bottom, bottom1)
                #maxtop    = max(top, top1)
                minbottom, maxtop = self.ax2.get_ylim()
                
                self.ax.set_ylim(minbottom, maxtop)
                self.ax1.set_ylim(minbottom, maxtop)
                self.canvas.draw()
            except Exception as e:
                QtWidgets.QMessageBox.warning(self, "warning", str(e)[:64])
                return

    def _erChenAlgo(self):
        self.oldData = []
        self.newData = []
        #sort get middle
        for i in range(len(self.rawData)//10):
            c = self.rawData[i*10:i*10+10]
            if len(c) == 10:
                c.sort()
                cc = (c[4]+c[5]+c[6]) // 3
                self.oldData.extend([cc]*10)
        #erChen
        for i in range(len(self.rawData) - 10):
            c = self.rawData[i:i+10]
            if len(c) == 10:
                minAB = sum(c)
                minAB = (minAB+5) // 10
                upAB = 0
                for i, cc in enumerate(c):
                    upAB += (cc * (i+1))
                if upAB >= minAB * 55:
                    upAB = upAB - minAB * 55
                    upAB //= 303
                    upAB //= 2
                    self.newData.append(minAB - upAB)
                else:
                    upAB = minAB * 55 - upAB
                    upAB //= 303
                    upAB //= 2
                    self.newData.append(minAB + upAB)
        return "least square method"

    def _elecRefreshAlgo(self):
        self.oldData = self.rawData
        self.newData = []
        self.newData.append(self.rawData[0])
        for i in range(1, len(self.rawData) - 5):
            c = self.rawData[i:i+5]
            flag = 0
            if len(c) == 5:
                if (c[0] < c[1] and c[1] < c[2] and c[2] < c[3] and c[3] < c[4]) or \
                    (c[0] > c[1] and c[1] > c[2] and c[2] > c[3] and c[3] > c[4]):
                    flag = 1
                if (flag == 1) or (self.newData[-1] > c[0] + 10 or self.newData[-1] < c[0] - 10):
                    self.newData.append(c[0])
        return "voltage refresh checked"

    def _elecCompensateAlgo(self):
        self.oldData = []
        self.newData = []
        return "voltage compensate"

    def _zeroFollow(self):
        self.oldData = []
        self.newData = []
        return "zero fllow"

    def _semiAlgorithmAlgo(self):
        self.oldData = self.rawData
        self.newData = []
        backReal = 0
        maxVot = 0
        for c in self.rawData:
            if c > backReal:
                maxVot = (c * 96) // 100
                backReal = c
            if c < (maxVot * 9) // 10:
                maxVot = 0
                backReal = 0
            else:
                c = maxVot
            self.newData.append(c)
        return "semiconductor algorithm"

    def _calcLineChrome(self, senVolt, curZero=400, ch0=0, calibV=800, calibCh=500):
        tmpChrome = ch0
        if senVolt > curZero and calibV > 0:
            tmp = calibCh - ch0             #chrome difference
            tmpChrome = senVolt - curZero   #volt difference
            tmp *= tmpChrome
            tmp //= calibV
            tmpChrome = tmp + ch0
        return tmpChrome

    def _autoCalibAlgo(self):
        self.oldData = []
        self.newData = []
        zero = 400
        calibChrome = 500
        calibVolt   = 800
        calibChrome2 = 700
        calibVolt2 = 7 * 90
        calibVolt2 *= calibVolt
        calibVolt2 //= calibChrome
        for c in self.rawData:
            #old single point calc
            tmp = c - zero if c > zero else 0
            if tmp <= calibVolt:
                tmpChrome = self._calcLineChrome(c)
            else:
                tmp -= calibVolt
                tmpChrome = (tmp * calibChrome) // 180 + calibChrome
                if tmpChrome > 1000: tmpChrome = 1000
            self.oldData.append(tmpChrome)
            #new double point calc
            if c > zero:
                tmp = c - zero
                if tmp <= calibVolt:
                    pass   #old already calc
                elif tmp > calibVolt and tmp < calibVolt2:
                    dema = calibVolt2 - calibVolt
                    tmpChrome = self._calcLineChrome(tmp, calibVolt, calibChrome, dema, calibChrome2)
                else:
                    tmpChrome = self._calcLineChrome(tmp, calibVolt2, calibChrome2, 180, 1000)
            else:
                tmpChrome = 0
            self.newData.append(tmpChrome)
        return "auto calib algorithm"

    def _infinityApproachAlgo(self):
        self.oldData = self.rawData
        self.newData = []
        cmp = [100, 250, 400, 600, 750, 900]
        curChrome = self.rawData[0]
        for c in self.rawData:
            tm = c
            for i in range(6):
                cm = cmp[i]
                if (c + 70 >= cm) and (c <= cm):
                    tm = cm - c
                    tm //= 4
                    tm += c
                    break
                elif (c > cm) and (c <= cm + 45):
                    tm = c - cm
                    tm //= 4
                    tm += c
                    break
            temp = curChrome
            if tm + 15 < curChrome:
                temp -= 15
            elif tm > curChrome + 15:
                temp += 15
            else:
                temp = tm
            self.newData.append(temp)
            curChrome = temp
        return "infinite approach algorithm"

    def _returnZeroAlgo(self):
        self.oldData = []
        self.newData = []
        for c in self.rawData:
            if c < 10.0: c = 0.0
            self.oldData.append(c)
            if c < 30.0303:
                c = 0.0
            elif c < 50.0:
                tc = (50.0 - c) * 1.5
                if c > tc:
                    c = c - tc
                else:
                    c = 0.0
            self.newData.append(c)
        return "return zero algorithm"

    def _MODRCFilter(self):
        self.oldData = []
        self.newData = []
        s_coefficient = 10
        s_direction_Flag = 0
        s_wave_direction_Flag = 0
        tmpADValue = 0
        tmpDirectionFlag = 0
        s_senADBuff = [0] * 8

        self.oldData = self.rawData

        curADValue = self.rawData[0]
        self.rawData = self.rawData[1:]
        for NewADValue in self.rawData:
            for i in range(7):
                s_senADBuff[7 - i] = s_senADBuff[6 - i]
            s_senADBuff[0] = NewADValue
            if ((curADValue >= NewADValue) and (NewADValue + 8 >= curADValue)) \
                    or ((curADValue <= NewADValue) and (curADValue + 8 >= NewADValue)):
                s_direction_Flag = 0
                s_wave_direction_Flag = 0
                s_coefficient = 10
                tmpADValue = curADValue
            else:
                tmpDirectionFlag = self.RCFilter_CheckDirection(s_senADBuff, 8, s_direction_Flag)
                if tmpDirectionFlag != s_direction_Flag:
                    if tmpDirectionFlag == 0:
                        s_direction_Flag = tmpDirectionFlag
                        s_coefficient = 10
                if s_direction_Flag == 1:
                    tmpADValue = c - curADValue
                    tmpADValue = (tmpADValue*s_coefficient) // 256
                    tmpADValue = curADValue + tmpADValue
                elif s_direction_Flag == 2:
                    tmpADValue = curADValue - NewADValue
                    tmpADValue = (tmpADValue*s_coefficient) // 256
                    tmpADValue = curADValue - tmpADValue
                else:
                    if NewADValue > curADValue:
                        if s_wave_direction_Flag != 1:
                            s_wave_direction_Flag = 1
                            s_coefficient = 10
                        tmpADValue = NewADValue - curADValue
                        tmpADValue = (tmpADValue*s_coefficient) // 256
                        tmpADValue = curADValue + tmpADValue
                    elif NewADValue < curADValue:
                        if s_wave_direction_Flag != 2:
                            s_wave_direction_Flag = 2
                            s_coefficient = 10
                        tmpADValue = curADValue - NewADValue
                        tmpADValue = (tmpADValue*s_coefficient) // 256
                        tmpADValue = curADValue - tmpADValue
                if s_direction_Flag == 1 or s_direction_Flag == 2:
                    s_coefficient += 50
                else:
                    s_coefficient += 10
                if s_coefficient > 256:
                    s_coefficient = 256
            self.newData.append(tmpADValue)
            curADValue = tmpADValue
        return "RCFilter"

    def RCFilter_CheckDirection(self, pData, dlen, curFlag):
        tmpUpCount = 0
        tmpDownCount = 0

        for i in range(dlen - 1):
            if pData[i] > pData[i+1]:
                tmpUpCount += 1
                if tmpDownCount > 0:
                    return 0
            elif pData[i] < pData[i+1]:
                tmpDownCount += 1
                if tmpUpCount > 0:
                    return 0
        if tmpUpCount > dlen // 4:
            return 1
        elif tmpDownCount > dlen // 4:
            return 2
        else:
            return curFlag

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dlg = QMySimulateDlg()
    dlg.show()
    sys.exit(app.exec_())

